package edu.umn.cs5115.scheduler.entities;

import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public final class TimeConflict {
    
    /**
     * Checks every one of the times in times1 for conflicts in times2.
     * @param times1 The first set of meeting times.
     * @param times2 The second set of meeting times.
     * @return true if any of the times overlap, false if none of them do.
     */
    public static boolean checkOverlap(Collection<MeetingTime> times1, Collection<MeetingTime> times2)
    {
        for(MeetingTime time1 : times1)
        {
            for(MeetingTime time2 : times2)
            {
                if (checkOverlap(time1, time2))
                    return true;
            }
        }
        return false;
    }
    
	public static boolean checkOverlap( MeetingTime time1, MeetingTime time2 ) {
		
		// first check if they meet on the same days of the week
		if ( !checkDayOverlap( time1.getMeetingDays(), time2.getMeetingDays() ) ) {
			//System.out.println("days of the week do not overlap");
			return false;
		}
		
		// check if the course start and end dates overlap
		if ( !checkDateOverlap( time1.getStartDate(), time1.getEndDate(), time2.getStartDate(), time2.getEndDate() ) ) {
			//System.out.println("dates do not overlap");
			//System.out.println(time1.toString());
			//System.out.println("vs.");
			//System.out.println(time2.toString());
			return false;
		}
		
		// checkTIMEoverlap
		if ( !checkDateOverlap( time1.getStartTime(), time1.getEndTime(), time2.getStartTime(), time2.getEndTime() ) ) {
			//System.out.println("times do not overlap");
			return false;
		}
		
		return true;
	}
	
	/**
	 * Checks for weekday overlap between the givern parameters 
	 * 
	 * e.g. { TUESDAY, THURSDAY } and { MONDAY, WEDNESDAY, FRIDAY } do not overlap
	 *      { FRIDAY } and { MONDAY, FRIDAY } do.
	 * 
	 * @param days1	The first set of days
	 * @param days2 The second set of days
	 * @return true if the sets overlap, false if they do not
	 */
	private static boolean checkDayOverlap( Set<Weekday> days1, Set<Weekday> days2 ) {
		Iterator<Weekday> iter1 = days1.iterator();
		
		// go through the first set of days, and check if they intersect with set 2
		while (iter1.hasNext()) {
			if ( days2.contains(iter1.next()))
				return true;
		}
		
		return false;
	}
	
	/**
	 * Checks for an overlap between two events
	 * 
	 * @param start1
	 * @param end1
	 * @param start2
	 * @param end2
	 * @return boolean true if events overlap, false if not
	 */
	private static boolean checkDateOverlap( Date start1, Date end1, Date start2, Date end2 ) {
            long begin = start1.getTime();
            long otherBegin = start2.getTime();
            long end = end1.getTime();
            long otherEnd = end2.getTime();
            
            // This logic is duplicated in Calendar.java!
            return ((otherBegin >= begin && otherBegin <= end) ||
                    (otherEnd   >= begin && otherEnd   <= end) ||
                    (begin >= otherBegin && end <= otherEnd));
	}

    /**
     * Finds all conflicts for the given section in the schedule.  Note that 
     * while section need not be chosen, only chosen sections in the schedule
     * will be found to conflict with it.
     * @param section The section whose times you want to check.
     * @param schedule The schedule with the other course
     */
    public static Set findConflicts(Section section, Schedule schedule)
    {
        HashSet<Section> set = new HashSet();
        Set<Course> courses = schedule.getCourses();
        
        for(Course course : courses)
            addConflictsForCoursePart(section, course, set);
        
        return set;
    }
    
    /**
     * Adds time conflicts for the given course part and all its children.
     * Conflicts are calculated against section, then added to the set conflictSet.
     * @param section The section to check for conflicts with.
     * @param coursePart The course part to check for time conflicts with.  This
     * also recursively checks against all of coursePart's children.
     * @param set The set to add any sections found to be conflicting to.
     */
    private static void addConflictsForCoursePart(Section section, CoursePart coursePart, Set conflictSet)
    {
        if (coursePart != section)
        {
            if (coursePart instanceof Section)
            {
                Section otherSection = (Section) coursePart;
                if (otherSection.isChosen())
                {
                    Set<MeetingTime> times1 = section.getMeetingTimes();
                    Set<MeetingTime> times2 = otherSection.getMeetingTimes();
                    if (checkOverlap(times1, times2))
                        conflictSet.add(otherSection);
                }
            }
            for(CoursePart child : coursePart.getChildren())
                addConflictsForCoursePart(section, child, conflictSet);
        }
    }
}
